﻿package MyDisplayObjects  {
	import flash.display.ColorCorrection;
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFormat;

	/** This class implements a clickable button that may change in color and/or text as 
	*	user mouses-over and presses-down.
	*/
	public class MyButton extends MovieClip{
		public static const AUTO_COLOR:uint = 0x112358;
		
		private static var X_BORDER:Number, Y_BORDER:Number;
		public var fontSize:Number;
		private var _enabled:Boolean=true,_selected:Boolean=false, _mouseOver:Boolean=false, _mouseDown:Boolean=false, disableOnClick:Boolean, selectOnClick:Boolean;
		private var color:uint, overColor:uint, downColor:uint, selectedColor:uint, disabledColor:uint, backColor:uint;
		private var textColor:uint, overTextColor:uint, downTextColor:uint, selectedTextColor:uint, disabledTextColor:uint;
		private var text:String, overText:String, downText:String, selectedText:String, disabledText:String;
		private var tField:TextField;
		private var faceWidth:Number, faceHeight:Number;
		private var ellipseWidth:Number, ellipseHeight:Number;
		private var backOffsetX:Number, backOffsetY:Number;
		private var face:MovieClip,back:MovieClip;
		/** Include the width, and height of the intended button (with the option to expand for text), set text and color for states of up, over, down 
			If user wants to use exact size of largest text use faceWidth=0, faceHeight=0
		*/
		//public function MyButton(faceWidth:Number, faceHeight:Number, fontSize:Number, ellipseWidth:Number, ellipseHeight:Number, upColor:uint = 0xFFFFFF, upText:String = "", upTextColor:uint=0x444444, overColor:uint = 0xCCCCCC, overText:String = "", overTextColor:uint=0x444444, downColor:uint = 0xAAAAAA, downText:String = "", downTextColor:uint=0x444444, resizeForText:Boolean=true, beginEnabled:Boolean = true, remainSelected:Boolean = true){
		public function MyButton(faceWidth:Number, faceHeight:Number, ellipseWidth:Number, ellipseHeight:Number, text:String, color:uint, numLines:uint=0, selectOnClick:Boolean = true, disableOnClick:Boolean = false, textColor:uint=AUTO_COLOR, overText:String = "", overColor:uint = AUTO_COLOR, overTextColor:uint=AUTO_COLOR, downText:String = "", downColor:uint = AUTO_COLOR, downTextColor:uint=AUTO_COLOR, selectedText:String = "", selectedColor:uint = AUTO_COLOR, selectedTextColor:uint=AUTO_COLOR, disabledText:String = "", disabledColor:uint = AUTO_COLOR, disabledTextColor:uint=AUTO_COLOR){
			this.color = color;
			this.text = text;
			this.disableOnClick = disableOnClick;
			this.selectOnClick = selectOnClick;
			this.faceWidth = faceWidth;
			this.faceHeight = faceHeight;
			this.ellipseHeight = ellipseHeight;
			this.ellipseWidth = ellipseWidth;
			X_BORDER = faceWidth/20;
			Y_BORDER = faceHeight/20;			
			
			var r:int, g:int, b:int;
			var tr:int, tg:int, tb:int; 
			var _r:int, _g:int, _b:int;
			r = color & 0xFF0000; r = r >> 16;
			g = color & 0x00FF00; g = g >> 8;
			b = color & 0x0000FF;
			var lightOrDark:int;
			var towardsRange:uint;
			var awayRange:uint;
			var maxComponent:uint = Math.max(r, Math.max(g, b));
			var minComponent:uint = Math.min(r, Math.min(g, b));
			if (maxComponent > 136 )
			{
				lightOrDark = 1;
				towardsRange = 255 - minComponent; // distance from pure color
				awayRange = maxComponent; // distance from black
			} else
			{
				lightOrDark = -1;
				towardsRange = maxComponent; // distance from pure black
				awayRange = 255 - minComponent; // distance from pure color
			}
			
			// If text color is not set make it 2/4 of the way to opposite pole from color
			if (textColor == AUTO_COLOR)
			{
				tr = r-lightOrDark*awayRange*3/4; if (tr < 0) tr = 0; if (tr > 255) tr = 255;
				tg = g-lightOrDark*awayRange*3/4; if (tg < 0) tg = 0; if (tg > 255) tg = 255;
				tb = b-lightOrDark*awayRange*3/4; if (tb < 0) tb = 0; if (tb > 255) tb = 255;
				this.textColor = tr << 16 | tg << 8 | tb;
			} else
			{
				this.textColor = textColor;
				tr = color & 0xFF0000; tr = tr >> 16;
				tg = color & 0x00FF00; tg = tg >> 8;
				tb = color & 0x0000FF;
			}
			
			// set over text
			if (overText == "")
			{
				this.overText = text;
			} else
			{
				this.overText = overText;
			}
			// If over color is not set make it 1/4 of the way closer to color's pole
			if (overColor == AUTO_COLOR)
			{
				_r = r+lightOrDark*towardsRange*1/4; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = g+lightOrDark*towardsRange*1/4; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = b+lightOrDark*towardsRange*1/4; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.overColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.overColor = overColor;
			}
			// If over text color is not set make it 1/4 of the way closer to color's pole
			if (overTextColor == AUTO_COLOR)
			{
				_r = tr+lightOrDark*towardsRange*1/4; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = tg+lightOrDark*towardsRange*1/4; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = tb+lightOrDark*towardsRange*1/4; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.overTextColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.overTextColor = overColor;
			}
			
			// set down text
			if (downText == "")
			{
				this.downText = text;
			} else
			{
				this.downText = downText;
			}
			// If down color is not set make it 1/4 of the way opposite to color's pole
			if (downColor == AUTO_COLOR)
			{
				_r = r-lightOrDark*awayRange*1/4; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = g-lightOrDark*awayRange*1/4; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = b-lightOrDark*awayRange*1/4; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.downColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.downColor = downColor;
			}
			// If down text color is not set make it 1/4 of the way closer to color's pole
			if (downTextColor == AUTO_COLOR)
			{
				_r = tr-lightOrDark*awayRange*1/4; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = tg-lightOrDark*awayRange*1/4; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = tb-lightOrDark*awayRange*1/4; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.downTextColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.downTextColor = downColor;
			}
			
			// set selected text
			if (selectedText == "")
			{
				this.selectedText = text;
			} else
			{
				this.selectedText = selectedText;
			}
			// If selected color is not set make it 1/8 of the way towards color's pole
			if (selectedColor == AUTO_COLOR)
			{
				_r = r+lightOrDark*towardsRange*1/8; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = g+lightOrDark*towardsRange*1/8; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = b+lightOrDark*towardsRange*1/8; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.selectedColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.selectedColor = selectedColor;
			}
			// If selected text color is not set make it 1/8 of the way closer to color's pole
			if (selectedTextColor == AUTO_COLOR)
			{
				_r = tr+lightOrDark*towardsRange*1/8; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = tg+lightOrDark*towardsRange*1/8; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = tb+lightOrDark*towardsRange*1/8; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.selectedTextColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.selectedTextColor = selectedColor;
			}
			
			// set disabled text
			if (disabledText == "")
			{
				this.disabledText = text;
			} else
			{
				this.disabledText = disabledText;
			}
			// If disabled color is not set make it 1/8 of the towards color's pole
			if (disabledColor == AUTO_COLOR)
			{
				_r = r+lightOrDark*towardsRange*1/8; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = g+lightOrDark*towardsRange*1/8; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = b+lightOrDark*towardsRange*1/8; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.disabledColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.disabledColor = disabledColor;
			}
			// If disabled text color is not set make it 1/4 of the way closer to color's pole
			if (disabledTextColor == AUTO_COLOR)
			{
				_r = tr-lightOrDark*awayRange*1/4; if(_r < 0) _r = 0; if (_r > 255) _r = 255;
				_g = tg-lightOrDark*awayRange*1/4; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
				_b = tb-lightOrDark*awayRange*1/4; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
				this.disabledTextColor = _r << 16 | _g << 8 | _b;
			} else
			{
				this.disabledTextColor = disabledColor;
			}
			
			// get shadow color			
			_r = r-1*r/2; if (_r < 0) _r = 0; if (_r > 255) _r = 255;
			_g = g-1*g/2; if (_g < 0) _g = 0; if (_g > 255) _g = 255;
			_b = b-1*b/2; if (_b < 0) _b = 0; if (_b > 255) _b = 255;
			backColor = _r << 16 | _g << 8 | _b;
			
			var textArr:Array = new Array(text, overText, downText, selectedText, disabledText);
			var maxWordsString:String = MyButton.maxWordsStringInArray(textArr);
			var maxString:String = MyButton.maxStringInArray(textArr);
			
			if (numLines == 0)
			{
				numLines = maxWordsString.split(" ").length;
			} 
			
			// setup face
			this.fontSize = Math.min(faceHeight-2*Y_BORDER, faceWidth-2*X_BORDER);
			face = new MovieClip();
			tField = new TextField();
			tField.selectable = false;
			tField.mouseEnabled = false;
			if (numLines > 1) tField.wordWrap = true;
			// find the largest allowable font size
			for (var f:uint=1; f <= this.fontSize; f++)
			{
				tField.defaultTextFormat = new TextFormat("Arial", f, 0x000000);
				tField.text = maxString;
				tField.height = faceHeight - 2*X_BORDER;
				tField.width = faceWidth - 2*Y_BORDER;
				//tField.autoSize = "left";
				if (tField.textWidth > faceWidth-2*X_BORDER || tField.textHeight > faceHeight-2*Y_BORDER)
				{
					f--;
					break;
				}
			}
			this.fontSize = f;
			// setup back
			back = new MovieClip();
			back.graphics.beginFill(backColor);
			backOffsetX = Math.min(faceWidth/14, faceHeight/14);
			backOffsetY = backOffsetX;
			back.graphics.drawRoundRect(0, 0,faceWidth,faceHeight,ellipseWidth,ellipseHeight);
			back.x = backOffsetX;
			back.y = backOffsetY;
			
			this.addChild(back);
			face.addChild(tField);
			this.addChild(face);	 
			
			face.addEventListener( MouseEvent.MOUSE_OVER, handleMouseOver);
			redraw();
			
			/*
			tField = new TextField();
			tField.selectable = false;
			tField.mouseEnabled = false;
			tField.text = text;
			tField.wordWrap = true;
			tField.defaultTextFormat = new TextFormat("Arial",fontSize,textColor);
			tField.autoSize = "left";
		
			//face.graphics.lineStyle(1,0x000000);
			face.graphics.beginFill(color);
			face.graphics.drawRoundRect(0,0,faceWidth,faceHeight,ellipseWidth,ellipseHeight);
			face.graphics.endFill();
			this.faceWidth = face.width;
			this.faceHeight = face.height;
			
			
			
			*/
			
		}
		
		/** Get the current text that is being displayed */
		public function get currentText():String
		{
			return tField.text;
		}
		/** Get the current state of interaction */
		public function get currentState():String
		{
			if (_selected){ return "selected";}
			else if (!_enabled){ return "disabled";}
			else if (_mouseDown) {return "mouseDown";}
			else if (_mouseOver) {return "mouseOver"}
			else { return "";}
		}
		
		override public function get enabled():Boolean{return _enabled;}
		/** When enabled place face on button, place mouse event on face */
		override public function set enabled(b:Boolean):void
		{
			if (!_enabled)
			{ // currently disabled
				if (b)
				{
					face.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
					_enabled = true;
					redraw();
				}				
			} else 
			{ // currently enabled
				if (!b)
				{
					this.removeAllListeners();
					_enabled = true;
					redraw();
				}
			}
		}
		/** Oppose of enabled */
		public function disabled(b:Boolean):void
		{
			this.enabled = !b;
		}
		public function get selected():Boolean{return _selected;}
		/** Remotely selects or deselects this button */
		public function set selected(b:Boolean):void
		{
			if (_selected)
			{  // currently selected
				if (!b)
				{ // deselect
					_selected = false;
					redraw();
				}				
			} else
			{ // currently not selected
				if (b)
				{
					enabled = true;
					_selected = true;
					redraw();
				}				
			}
		}
		/** Remotely de-selects this button */
		public function set deselected(b:Boolean):void
		{
			this.selected = !b;
		}
		
		private function handleMouseOver(event:MouseEvent):void
		{
			event.stopPropagation(); // stop so we can re-dispatch after activities
			face.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
			_mouseOver = true;
			redraw();
			//place new listeners on face
			face.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOut);
			face.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OVER));
		}
		private function handleMouseOut(event:MouseEvent):void
		{
			event.stopPropagation(); // stop so we can re-dispatch after activities
			face.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOut);
			face.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			_mouseOver = false;
			redraw();
			//place new listeners on face
			face.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
			this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT));
		}
		private function handleMouseDown(event:MouseEvent):void
		{
			event.stopPropagation(); // stop so we can re-dispatch after activities
			face.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			_mouseDown = true;
			redraw();			
			face.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
			face.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutWhileDown);
			this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN));
		}
		/** Cancel this press */
		private function handleMouseOutWhileDown(event:MouseEvent):void
		{
			event.stopPropagation(); // stop so we can re-dispatch after activities
			face.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
			face.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutWhileDown);
			_mouseOver = false;
			_mouseDown = false;
			redraw();
			//place new listeners on face
			face.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
			this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT));
		}

		private function handleMouseUp(event:MouseEvent):void
		{
			event.stopPropagation(); // stop so we can re-dispatch after activities
			face.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
			face.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutWhileDown);
			_mouseDown = false;
			if (this.selectOnClick)
			{
				selected = true;
				face.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOut);
			} else if (this.disableOnClick)
			{
				enabled = false;
			} else
			{
				redraw();
				face.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOut);
			}
			this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
			this.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
		}
		
		/////////////////////////////////// DRAW          ///////////////////////////////////////////
		/** Draw based on current color */
		private function redraw():void
		{
			var thisColor:uint;
			var thisTextColor:uint;
			var thisText:String;
			
			if (!_enabled)
			{
				face.x = 0; face.y = 0;	
				thisColor = disabledColor;
				thisTextColor = disabledTextColor;
				thisText = disabledText;
			} else if (_selected)
			{
				face.x = 0; face.y = 0;	
				thisColor = selectedColor;
				thisTextColor = selectedTextColor;
				thisText = selectedText;
			} else if (_mouseDown)
			{
				face.x = backOffsetX; face.y = backOffsetY;	
				thisColor = downColor;
				thisTextColor = downTextColor;
				thisText = downText;
			} else if (_mouseOver)
			{
				face.x = 0; face.y = 0;
				thisColor = overColor;
				thisTextColor = overTextColor;
				thisText = overText;
			} else 
			{
				face.x = 0; face.y = 0;
				thisColor = color;
				thisTextColor = textColor;
				thisText = text;
			}
			face.graphics.clear();
			face.graphics.beginFill(thisColor);
			face.graphics.drawRoundRect(0,0,faceWidth,faceHeight,ellipseWidth,ellipseHeight);
			face.graphics.endFill();
			tField.defaultTextFormat = new TextFormat("Arial",fontSize,thisTextColor);
			tField.text = thisText;
			tField.autoSize = "left";
			tField.x = (faceWidth - tField.textWidth)/2;
			tField.y = (faceHeight - tField.textHeight)/2;
		}
		
		/** Removes listeners for disabling */
		private function removeAllListeners ():void
		{
			if (face.hasEventListener(MouseEvent.MOUSE_OVER)) face.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
			if (face.hasEventListener(MouseEvent.MOUSE_OUT)) face.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOut);
			if (face.hasEventListener(MouseEvent.MOUSE_DOWN)) face.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			if (face.hasEventListener(MouseEvent.MOUSE_UP)) face.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
		}
		
		private static function maxStringInArray(arr:Array):String
		{
			var cs:String = "";
			for each (var s:String in arr)
			{
				if (s.length > cs.length) cs = s;
			}
			return cs;
		}
		private static function maxWordsStringInArray(arr:Array):String
		{
			var cs:String = "";
			var max:int = 0;
			for each (var s:String in arr)
			{
				if (s.split(" ").length > max) {max = s.split(" ").length; cs = s;}
			}
			return cs;
		}
	}
}	

	
